const app = getApp();

const OPEN_DOMAIN = 'https://open.ys7.com';

// 生成uuid
const wxuuid = function () {
  var s = [];
  var hexDigits = "0123456789abcdef";
  for (var i = 0; i < 36; i++) {
    s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  }
  s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
  s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
  s[8] = s[13] = s[18] = s[23] = "-";
 
  var uuid = s.join("");
  return uuid
};

Component({
  properties: {
    // 这里定义了innerText属性，属性值可以在组件使用时指定
    accessToken: {
      type: String,
      observer: function (newVal) {
        console.log('accessToken', newVal);
        this.setData({
          accessToken: newVal
        })
      }
    },
    deviceSerial: {
      type: String,
      observer: function (newVal) {
        console.log('deviceSerial', newVal);
        this.setData({
          deviceSerial: newVal
        })
      }
    }, 
    channelNo: {
      type: String,
      observer: function (newVal) {
        console.log('channelNo', newVal);
        this.setData({
          channelNo: newVal
        })
      }
    }, 
  },
  data: {
    // 对讲过程
    accessToken: '', // 由外部传入
    deviceSerial: '', // 由外部传入
    channelNo: '', // 由外部传入
    devProtocol: '', // 国标设备  // 扩展字段
    token1: '', // 用于邀请设备入会
    token2: '', // 用于强制设备退会
    websocketUrl: '',
    customId: 'isZhangqianwei', // 开发者自定义用户id，以此换取clientId
    clientId: 993089712, // 注意：通讯过程中真实的clientId，此处仅供调试，无需赋值
    roomId: 0, // 注意：需要加入的房间号，需外部提供
    joinPassword: '', // 注意：加入房间的密码，需外部提供
    vtmHttpsAddress: '', 
    vtmIp: 'test11.ys7.com', // 此处仅供调试，无需赋值
    vtmPort: 8554, // 此处仅供调试，无需赋值
    rtmpAddress: '', // push/play地址
    rtmpPort: '', // 推流拉流端口地址
    lockReconnect: false, // websocket重连
    limit: 0,
    timer: null,
    pushVideo: true, // 推流
    pushVideoContext: null,
    pushUrl: '', // 推流地址
    livePlayerContext: [
      
    ], // 播放节点
    showVideoControls: false,
    videoNetWorkError: false,
    playSrc: '', // 播放地址
    

    websocketHeartTimer: null, // 增加服务端
    websocketHeartpongCount: 0, // 服务端响应心跳次数
    websocketHeartpingCount: 0, //客户端发送心跳次数
  },
  
  lifetimes: {
    attached: function() {
      // 初始化push
      var pushcontext = wx.createLivePusherContext('livePusher', this);
      this.setData({
        pushVideoContext: pushcontext
      });
      // 初始化当前用户id
      const uuid = wxuuid();
      this.setData({
        customId: uuid
      });
    },
	ready: function(){
		// 因对讲模式中，无画面，因此若需要画面，则可以不从websocket中取拉流地址，可以通过该接口获取地址
       this.getPlayUrl();
	},
    detached: function() {
      this.heartCheckReset();
      this.websocketClose()
    }
  
  },

  methods: {
    getPlayUrl(){
      const { accessToken, deviceSerial, channelNo } = this.data;
      var _this = this;
      wx.request({
        url: `${OPEN_DOMAIN}/api/lapp/v2/live/address/get`, //仅为示例，并非真实的接口地址
        method: 'POST',
        data: {
          accessToken: accessToken,
          deviceSerial: deviceSerial,
          channelNo: channelNo,
          expireTime: 86400,
          quality: 1,
          protocol: 3,
        },
        header: {
          'content-type': 'application/x-www-form-urlencoded' // 默认值
        },
        success: (res) => {
          console.log(res)
          if(res.data.code ==200 && res.data.data && res.data.data.url){
            var result = res.data;
            if(result.code == 200){
              _this.setData({
                playSrc: result.data.url,
              })
            }
          }else {
            console.log("获取播放地址失败")
          }
        }
      })
    },
    // 检测当前网络状态
    checkNetWork(){
      const _this = this;
      wx.getNetworkType({
        success (res) {
          const networkType = res.networkType
          if(!networkType || networkType === 'none'){
            wx.showToast({
              title: '当前网络异常',
              icon: 'none',
              duration: 2000,
            })
          }
        }
      })
    },

    // 打印当前时间
    getTime () {
      const date = new Date();
      const hour = date.getHours();
      const minute = date.getMinutes();
      const second = date.getSeconds();
      console.log(hour + ':' + minute + ':' + second);
    },
    // 退出对讲
    exitTalk () {
      this.setData({
        pushUrl: '',
        playSrc: ''
      });
      this.websocketClose();
    },
    // 开始对讲-①获取设备能力集

  startTalk(){
    const {accessToken, deviceSerial} = this.data;
    wx.request({
      url: `${OPEN_DOMAIN}/api/lapp/device/capacity`,
      method: 'POST',
      data: {
        accessToken: accessToken,
        deviceSerial: deviceSerial,
      },
      header: {
        'content-type': 'application/x-www-form-urlencoded', // 默认值
      },
      success:(res) => {
        console.log('设备能力集:',res.data);
        if (res.data.code == 200) {
          const supportTalk = res.data.data.support_talk;
          console.log('supportTalk', supportTalk);
          if (supportTalk == 1 || 4) {
            // 设备能力集中为1/4表示支持全双工
            this.getToken()
          } else {
            wx.showToast({
              title: '设备不支持对讲',
              icon: 'none'
            })
          }
        }
      },
      fail: (err)=>{
        console.log(err);
      }
    })
  },

  // 对讲-②获取两个取流token
  getToken(){
    const {accessToken} = this.data;
    wx.request({
      url: `${OPEN_DOMAIN}/api/user/token`,
      method: 'POST',
      data: {
        accessToken: accessToken,
        count: 2,
        clientType: "0",
        featureCode:"123",
        osVersion:"2.3.6",
        sdkVersion:"v.1.0.20140720",
        netType:"UNKNOWN"
      },
      header: {
        'content-type': 'application/x-www-form-urlencoded', // 默认值
      },
      success:(res) => {
        console.log('取流token:',res.data);
        if (res.data.resultCode == 200) {
          const tokens = res.data.tokenArray;
          if (tokens && tokens.length == 2) {
            this.setData({
              token1: tokens[0],
              token2: tokens[1]
            });
            // 创建房间，并查询房间信息
            this.orderRoom()
          }
        }
      },
      fail: (err)=>{
        console.log(err);
      }
    })
  },

  // 对讲-③创建房间并查询房间信息
  orderRoom(){
    // 检查网络状态
    this.checkNetWork();

    const { customId, accessToken } = this.data;
    wx.request({
      url: `${OPEN_DOMAIN}/api/v3/conference/gen`,
      method: 'POST',
      data: {
        accessToken: accessToken,
        customId: customId,
      },
      header: {
        'content-type': 'application/x-www-form-urlencoded', 
      },
      success:(res) => {
        console.log('预定创建并查询房间信息:',res.data);
        const code =  res.data.code || res.data.meta.code
        if (code == 200) {
          const clientId = res.data.clientId;
          const conferenceInfos = res.data.conferenceInfos;
          const roomId = conferenceInfos.roomId;
          const vtmHttpsAddress = conferenceInfos.vtmHttpsAddress;
          const vtmAddress = conferenceInfos.vtmAddress;
          let vtmIp = '', vtmPort = '';
          if (vtmAddress && vtmAddress.length > 0) {
            vtmIp = vtmAddress.split(':')[0];
            vtmPort = vtmAddress.split(':')[1];
          }
          this.setData({
            clientId: clientId,
            vtmHttpsAddress: vtmHttpsAddress,
            vtmIp: vtmIp,
            vtmPort: vtmPort,
            roomId: roomId
          }, () => {
            const {clientId, vtmHttpsAddress,vtmIp, vtmPort} = this.data;
            console.log('获取房间信息-','clientId:',clientId ,'vtmHttpsAddress:', vtmHttpsAddress, 'vtmIp:',vtmIp,'vtmPort:',vtmPort);
            this.advanceDevice()
          })
        } else if (code == '10002') {
          wx.showToast({
            title: 'accessToken过期或异常',
            icon: 'none'
          })
        } else if (code == '404') {
          wx.showToast({
            title: '未找到房间信息',
            icon: 'none'
          })
        } else  {
          wx.showToast({
            title: '创建房间-'+res.data.meta.message,
            icon: 'none'
          });
          this.setData({
            talkStatus: 3 // 启动失败
          })
        }
      },
      fail: (err)=>{
        console.log(err);
      }
    })
  },

  // 对讲-④邀请设备入会
  advanceDevice() {
    const {accessToken, roomId, deviceSerial, channelNo, token1, devProtocol} = this.data;
    console.log('当前设备协议：', devProtocol);
    wx.request({
      url: `${OPEN_DOMAIN}/api/v3/conference/device/invite`,
      method: 'POST',
      data: {
        accessToken: accessToken,
        roomId: roomId,
        deviceSerial: deviceSerial,
        channelNo: channelNo,
        mode: 2,
        authType: 5,
        token: token1,
        devProtocol: devProtocol
      },
      header: {
        'content-type': 'application/x-www-form-urlencoded', // 默认值
      },
      success:(res) => {
        console.log('邀请设备入会:',res.data);
        if (res.data.meta.code == 200) {
          console.log('邀请设备入会成功，加入模式为对讲模式');
          this.getWebsocket()
        } else {
          if (res.data.meta.code == 80502) {
            this.setData({
              talkStatus: 4 // 设备对讲中
            })
          } else {
            wx.showToast({
              title: '邀请设备入会-'+ res.data.meta.message,
              icon: 'none'
            });
            this.setData({
              talkStatus: 3 // 启动失败
            })
          }
          
        }
      },
      fail: (err)=>{
        console.log(err);
        this.setData({
          talkStatus: 3 // 启动失败
        })
      }
    })

  },

  // 对讲-⑤强制设备退会
  exitDevice() {
    const {accessToken, roomId, deviceSerial, channelNo, token2} = this.data;
    wx.request({
      url: `${OPEN_DOMAIN}/api/v3/conference/device/kickout`,
      method: 'POST',
      data: {
        accessToken: accessToken,
        roomId: roomId,
        deviceSerial: deviceSerial,
        channelNo: channelNo,
        authType: 5,
        token: token2
      },
      header: {
        'content-type': 'application/x-www-form-urlencoded', // 默认值
      },
      success:(res) => {
        console.log('强制设备退出房间:',res.data);
        if (res.data.meta.code == 200) {
          console.log('强制设备退出房间成功');
          // this.getWebsocket()
        } else {
          wx.showToast({
            title: '强制设备退出房间-'+ res.data.meta.message,
            icon: 'none'
          });
        }
      },
      fail: (err)=>{
        console.log(err);
      }
    })

  },

      // 获取websocket地址
  getWebsocket () {
    const { vtmHttpsAddress, roomId } = this.data;
    
    const url = 'https://' + vtmHttpsAddress + '/vtm/rtmpavc/' + roomId;   // 注意：此处需配置小程序域名 
    wx.request({
      url: url,
      method: 'GET',
      header: {
        'content-type': 'application/x-www-form-urlencoded', // 默认值
      },
      success:(res) => {
        console.log('vtm连接返回：',res.data);
        if (res.data.retcode == 0 || res.data.retcode == 200) {
          const wsurl = 'wss://' + res.data.serverAddr + ':' + res.data.serverPort+'/rtmpavc'; 
          
          this.setData({
            websocketUrl: wsurl,
            rtmpAddress: res.data.serverAddr
          }, () => {
            // 连接websocket
            this.socketConfig();
          })
        } else if (res.data.retcode == 503) {
          wx.showToast({
            title: '无可用服务',
            icon: 'none'
          });
          this.setData({
            talkStatus: 3 // 启动失败
          });
          this.exitDevice()
        } else {
          console.log('获取websocket地址失败', res.data.retcode);
          wx.showToast({
            title: '获取websocket地址失败',
            icon: 'none'
          });
          this.setData({
            talkStatus: 3 // 启动失败
          });
          this.exitDevice()
        }
      },
      fail: (err)=>{
        console.log('获取websocket地址失败', err);
        wx.showToast({
          title: '获取websocket地址失败',
          icon: 'none'
        });
        this.setData({
          talkStatus: 3 // 启动失败
        })
      }
    })
  },

  // 配置socket
  socketConfig () {
    let that = this;
    const { websocketUrl, clientId } = this.data;
    if (Object.keys(app.globalData.socketTask).length <= 0 || app.globalData.socketTask.readyState === 3) {
      console.log('开始连接socket');
      // socketTask的属性值readyState，取下面4个状态值：CONNECTING: 0 连接中 、OPEN: 1 已连接 、CLOSING: 2 关闭中 、CLOSED: 3 已关闭
      // 直接判断SocketTask对象的属性值readyState，如果是1的话就表示连接可用。
      console.log('websocketURL:', websocketUrl);
      let socketTask = null;
    
      const websocketConnect = new Promise((resolve, reject) => {
        socketTask = wx.connectSocket({
          url: websocketUrl,
          // url: 'wss://test12jsdecoder.ys7.com/?version=0.1&cipherSuites=0&sessionID=',
          success: res => {
            console.log('connectSocket连接中', res);
            wx.showToast({
              title: 'websocket连接中,请稍候',
              icon: 'none'
            });
            resolve(res);
          },
          fail: err => {
            console.log('connectSocket失败', err);
            wx.showToast({
              title: 'webSocket连接失败',
              icon: 'none'
            });
            this.setData({
              talkStatus: 3 // 启动失败
            });
            this.exitDevice()
          }
        });
      });

      websocketConnect.then(data => {
        if (data.errMsg == 'connectSocket:ok') {
          app.globalData.socketTask = socketTask;
          that.initEventHandle();
        }
      });
      
    }
  },

  // 初始化websocket事件
  initEventHandle() {
    const socketTask = app.globalData.socketTask;
    const that = this;
    const { clientId, roomId, joinPassword, vtmIp, vtmPort, rtmpAddress } = this.data;
    
    socketTask.onOpen((res) => {
      console.log('监听WebSocket开启', res);
      // 打开bav会话
      let param = {
        cmdType:1000,
        vtmIp: vtmIp,
        vtmPort: parseInt(vtmPort),
        clientId: clientId,
        roomId: roomId,
        authentication: `roomId=${roomId}&password=${joinPassword}`,
        mode: 2
      };

      if (socketTask.readyState === 1) {
        wx.sendSocketMessage({
          data: JSON.stringify(param)
        });
        console.log('websocket发送给服务端的消息:', param)
      }
    });


    socketTask.onMessage((res) => {
      console.log('接收到WebSocket消息', res);
      
      if (res.data) {
        const result = JSON.parse(res.data);
        
        if (result.cmdType == '1003') {
          // 微信小程序推流
          const rtmpPort = result.rtmpPort;
          const rtmpIp = result.rtmpIp ? result.rtmpIp : rtmpAddress;
          const pushUrl = `rtmp://${rtmpIp}:${rtmpPort}/livestream/wechat?roomId=${roomId}&pushClientId=${clientId}`;
          console.log('推流地址:', pushUrl);

          that.setData({
            pushUrl: pushUrl,
          }, () => {
            var pushcontext = wx.createLivePusherContext('livePusher', this);
            that.setData({
              pushVideoContext: pushcontext
            });
            // 开始推流
            that.startPush()
          });

        } 
        else if (result.cmdType == '1002') 

        {
          // 微信小程序拉流  因对讲模式中，无画面，因此若需要画面，则可以通过getPlayUrl方法获取地址
        
          const playClientId = result.playClientId;
          const rtmpPort = result.rtmpPort;
          const rtmpIp = result.rtmpIp ? result.rtmpIp : rtmpAddress;
          const playSrc = `rtmp://${rtmpIp}:${rtmpPort}/livestream/wechat?roomId=${roomId}&pushClientId=${clientId}&playClientId=${playClientId}`;
          console.log('拉流地址:', playSrc);
          this.setData({
            //playSrc: playSrc
          }, () => {
              
              const playcontext = wx.createLivePlayerContext('talk-liveplayer', that);        
              console.log('当前拉流窗口~~~~~', playcontext);
              // 播放
              playcontext.play({
                success: ()=>{
                  console.log('播放对讲音频成功');
                },
                fail: (error)=>{
                  console.log('播放对讲音频失败：',error);
                }
              })
          });
        } 
        else if (result.cmdType == '1005') {
          // 某端退出房间
          const curPlayClientId = 'paly' + result.playClientId;
          wx.showToast({
            title: result.playClientId + '将关闭',
            icon: 'none'
          });
          this.setData({
            talkStatus: 3 // 对讲失败
          });
          // 对应播放端停止播放
          console.log('当前退出窗口', curPlayClientId);
          

        }
        else if (result.cmdType == '1001') {
          const code = result.code;
          const msg = result.msg;

          console.log('websocket返回的msg',code + '-->'+ msg);
          let content = '';
          
          switch (code) {
            case 0:
              break;
            case 10001:
              content = 'websocket请求参数错误';
              break;
            case 10002:
              content = 'flv转封装失败';
              break;
            case 10003:
              content = 'rtp转封装失败';
              break;
            case 10004:
              content = 'rtmp读写错误	';
              break;
            case 10005:
              content = 'rtmp接收缓存溢出';
              break;
            case 6:
              content = '连接sts服务失败';
              break;
            case 11:
              content = '无效的房间号';
              break;
            case 14:
              content = 'sts连接vtm失败';
              break;
            case 17:
              content = '房间已满';
              break;
            case 18:
              content = 'auth认证失败';
              break;
            case 35:
              content = '房间号不存在';
              break;
          }
          if (content) {
            wx.showToast({
              title: content,
              icon: 'none',
              duration: 300
            });
            // 关闭websocket
            that.websocketClose();
            this.setData({
              talkStatus: 3 // 对讲失败
            });
          }
          
        }
      }
    })

    socketTask.onClose((res) => {
      console.log('WebSocket已关闭', res);
      wx.showToast({
        title: 'websocket连接已关闭',
        icon: 'none'
      })
      // 监听到websocket关闭,关闭推流
      this.stopPush();
      // 强制
      this.exitDevice();
      this.setData({
        talkStatus: 3 // 对讲失败
      });
    })

    socketTask.onError((err) => {
      console.log('WebSocket连接失败', err)
      wx.showToast({
        title: 'WebSocket连接失败',
        icon: 'none'
      });
      // 强制
      this.exitDevice();
      this.setData({
        talkStatus: 3 // 对讲失败
      });
    })
  },

  // 关闭websocket
  websocketClose () {
    // 关闭websocket连接
    const socketTask = app.globalData.socketTask;
    const that = this;
    this.exitDevice()
    const { clientId, roomId } = this.data;
    if (Object.keys(socketTask).length === 0) {
      return
    }
    
    // 关闭bav会话
    let param ={
      cmdType:1004, // 通知服务端断开websocket
      clientId: clientId,
      roomId: roomId,
    };
    wx.sendSocketMessage({
      data: JSON.stringify(param)
    });
    console.log('websocket发送给服务端的消息:', param)
    
    socketTask.close({
      code: 1000,
      success: (res) => {
        console.log('websocket已断开连接', res)
      }
    })
  },


  // push
  pusherStateChange: function(e){
    console.log('>>> live-pusher onPushStateChange:', e.detail.code);
    const code = e.detail.code;
    let content = '';
    switch (code) {
      case 1001:
        console.log('code', code, '已经连接推流服务器');
        break;
      case 1002:
        console.log('code', code, '已经与服务器握手完毕,开始推流');
        break;
      case 1003:
        console.log('code', code, '打开摄像头成功');
        break;
      case 1004:
        console.log('code', code, '录屏启动成功');
        break;
      case 1005:
        console.log('code', code, '推流动态调整分辨率');
        break;
      case 1006:
        console.log('code', code, '推流动态调整码率');
        break;
      case 1007:
        console.log('code', code, '首帧画面采集完成, 推流成功');
        break;
      case 1008:
        console.log('code', code, '编码器启动');
        break;
      case -1301:
        console.log('code', code, '打开摄像头失败');
        break;
      case -1302:
        console.log('code', code, '打开麦克风失败');
        break;
      case -1303:
        console.log('code', code, '视频编码失败');
        break;
      case -1304:
        console.log('code', code, '音频编码失败');
        break;
      case -1306:
        console.log('code', code, '不支持的音频采样率');
        break;
      case -1307:
        console.log('code', code, '网络断连，且经多次重连抢救无效，更多重试请自行重启推流');
        content = '网络出错，推流失败';
        break;
      case -1308:
        console.log('code', code, '开始录屏失败，可能是被用户拒绝');
        break;
      case -1309:
        console.log('code', code, '录屏失败，不支持的Android系统版本，需要5.0以上的系统');
        break;
      case -1310:
        console.log('code', code, '录屏被其他应用打断了');
        break;
      case -1311:
        console.log('code', code, 'Android Mic打开成功，但是录不到音频数据');
        break;
      case -1312:
        console.log('code', code, '录屏动态切横竖屏失败');
        break;
      case 1101:
        console.log('code', code, '网络状况不佳：上行带宽太小，上传数据受阻');
        break;
      case 1102:
        console.log('code', code, '网络断连, 已启动自动重连');
        break;
      case 1103:
        console.log('code', code, '硬编码启动失败,采用软编码');
        break;
      case 1104:
        console.log('code', code, '视频编码失败');
        break;
      case 1105 || 1106:
        console.log('code', code, '新美颜软编码启动失败，采用老的软编码');
        break;
      case 3001:
        console.log('code', code, 'RTMP -DNS解析失败');
        break;
      case 3002:
        console.log('code', code, 'RTMP服务器连接失败');
        break; 
      case 3003:
        console.log('code', code, 'RTMP服务器握手失败');
        break;  
      case 3004:
        console.log('code', code, 'RTMP服务器主动断开，请检查推流地址的合法性或防盗链有效期');
        break; 
      case 3005:
        console.log('code', code, 'RTMP 读/写失败');
        break;
      default:
        console.log('未知错误')
        break;
    };
    wx.showToast({
      title: content,
      icon: 'none'
    })
  },

  stopPush(){
    console.log('停止推流');
    this.data.pushVideoContext.stop();
    this.setData({
      pushVideo: false
    })
  },

  startPush(){
    console.log('开始推流，推流地址为:', this.data.pushUrl);
    wx.showToast({
      title: '开始推流，推流地址为:',
    })
    wx.showToast({
      title: this.data.pushUrl,
    });
    this.setData({
      pushVideo: true
    })
    this.data.pushVideoContext.start();
    
  },


  error(e) {
    console.log('live-player',e);
    console.error('live-player error:', e.detail.errMsg)
    if(e.detail.errCode == 10001){
      wx.showToast({
        title: '视频播放或录制需要你手机授权微信录音或麦克风权限',
        icon:'none',
        duration:3000,
      })
    }
  },

  autoPlay(curPlayerContext) {
    this.checkNetWork();
    curPlayerContext.play({
      success: ()=>{
        console.log('播放对讲音频成功');
        
      },
      fail: (error)=>{
        
        console.log('播放对讲音频失败：',error);
      }
    })
  }
}

})